package com.rd.config;

import com.rd.config.security.*;
import com.rd.pojo.Admin;
import com.rd.pojo.Role;
import com.rd.service.IAdminService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.annotation.Resource;
import java.util.List;

/**
 * Spring Security配置类
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private static final String[] pass = {"/login", "/logout","/api/**"};


    @Bean
    public JwtAuthencationTokenFilter jwtAuthencationTokenFilter() {
        return new JwtAuthencationTokenFilter();
    }

    /**
     * 认证失败入口点
     */
    @Resource
    RestAuthorizationEntryPoint authorizationEntryPoint;

    /**
     * 没有权限的处理器
     */
    @Resource
    RestfulAccessDeniedHandler accessDeniedHandler;


    /**根据用户请求url,获取这个url所需要的Role,把Role装入到colletion中*/
    @Resource
    CustomFilter customFilter;

    /**判断当前用户是否具备指定的角色,是否拥有访问url对应的角色*/
    @Resource
    CustomUrlDecisionManager customUrlDecisionManager;


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //基于jwt,不需要csrf
        http.cors().configurationSource(configurationSource()).and().
                csrf().disable()
                //基于jwt,需要关闭session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                // 在pass数组中放行
                .antMatchers(pass)
                .permitAll()
                //其他请求都需要认证
                .anyRequest()
                .authenticated()
                //动态权限配置,在FilterSecurityInterceptor拦截器之前执行过滤器
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O  object) {
                        //获取请求的url,判断这个url需要什么角色才能访问,把角色装入到一个Collection中
                        object.setSecurityMetadataSource(customFilter);
                        //将当前登录的role和刚刚装入集合中role进行匹配,如果当前拥有就可以访问这个url否则不能访问
                        object.setAccessDecisionManager(customUrlDecisionManager);
                        return object;
                    }
                })
                //关闭请求头缓存
                .and()
                .headers().cacheControl();

        /**在用户名密码认证过滤器之前执行,jwt认证*/
        http.addFilterBefore(jwtAuthencationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
        /**添加未授权或者未认证通过的入口点*/
        http.exceptionHandling()
                //没有权限
                .accessDeniedHandler(accessDeniedHandler)
                //未登录
                .authenticationEntryPoint(authorizationEntryPoint);
    }


    //配置跨域请求
    CorsConfigurationSource configurationSource(){
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedHeader("*");//设置被允许的请求头字段，*表示允许所有字段
        corsConfiguration.addAllowedMethod("*");//设置允许的请求方法，*表示允许所有请求方法
        corsConfiguration.addAllowedOrigin("http://localhost:8080");//设置允许的域，*表示允许所有的域
        corsConfiguration.addAllowedOrigin("http://localhost:8081");
        corsConfiguration.setAllowCredentials(true);
        corsConfiguration.setMaxAge(3600L);//设置预检请求有效期，有效期内不必再次发送预检请求，默认是1800秒
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**",corsConfiguration);
        return source;
    }


    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //设置密码加密
        auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
    }

    @Resource
    private IAdminService adminService;

    @Override
    @Bean
    public UserDetailsService userDetailsService() {
        return new UserDetailsService() {
            @Override
            public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                //从t_admin表中查询用户信息
                Admin admin = adminService.getAdminInfo(username);
                if (admin != null) {
                    //调用根据adminId获取用户角色的方法
                    List<Role> roleList = adminService.getRoles(admin.getId());
                    admin.setRoles(roleList);
                    return admin;
                }
                throw new UsernameNotFoundException("用户名错误!");
            }
        };
    }


    @Override
    public void configure(WebSecurity web) throws Exception {
        //静态资源放行
        web.ignoring().antMatchers(
                "/login",
                "/logout",
                "/css/**",
                "/js/**",
                "/index.html",
                "favicon.ico",
                "/doc.html",
                "/webjars/**",
                "/swagger-resources/**",
                "/v2/api-docs/**",
                "/ws/**",
                "/api/**",
                "/captcha"
        );
    }


}
